

pub mod db;
pub mod test;
pub mod user;
pub mod article;




// i18n
rust_i18n::i18n!("locales", fallback = "zh-CN");



#[derive(serde::Deserialize)]
struct WebConfigDBTable
{
    url: String,
    username: Option<String>,
    password: Option<String>
}


#[derive(serde::Deserialize)]
struct WebConfigServerRouteTable
{
    r#type: String,
    path: String,
    fn_name: String
}



#[derive(serde::Deserialize)]
struct WebConfigServerTable
{
    address: [u8; 4],
    port: u16,
    route: Option<toml::value::Table>
}

#[derive(serde::Deserialize)]
struct WebConfigTable
{
    db: WebConfigDBTable,
    server: WebConfigServerTable
}

#[derive(serde::Deserialize)]
pub struct WebConfig {
    locale: Option<String>,
    dev: Option<WebConfigTable>,
    product: Option<WebConfigTable>,
}


macro_rules! parse_web_config_route {
    () => {

    };
}


pub fn read_web_configuration(path: &str) -> WebConfig
{
    let content = ::std::fs::read_to_string(path)
        .expect("read web configuration error");
    let configuration: WebConfig = toml::from_str(&content)
        .expect("read web configuration error");
    configuration
}


pub(crate) fn deal_db_url(db_cfg: &WebConfigDBTable) -> String
{
    if db_cfg.username.is_none() || db_cfg.password.is_none() {
        return db_cfg.url.clone();
    }
    let u_and_p = format!("{}:{}@", db_cfg.username.as_ref().unwrap(), db_cfg.password.as_ref().unwrap());
    let url_vec: Vec<&str> = db_cfg.url.split("://").collect();
    format!("{}://{}{}", url_vec[0], u_and_p, url_vec[1])
}



pub async fn init_session_layer() -> axum_session::SessionLayer<axum_session::SessionPgPool>
{
    let session_config = axum_session::SessionConfig::default()
        .with_table_name("sessions_table")
        // 'Key::generate()' will generate a new key each restart of the server.
        // If you want it to be more permanent then generate and set it to a config file.
        // If with_key() is used it will set all cookies as private, which guarantees integrity, and authenticity.
        .with_key(axum_session::Key::generate())
        // This is how we would Set a Database Key to encrypt as store our per session keys.
        // This MUST be set in order to use SecurityMode::PerSession.
        .with_database_key(axum_session::Key::generate())
        // This is How you will enable PerSession SessionID Private Cookie Encryption. When enabled it will
        // Encrypt the SessionID and Storage with an Encryption key generated and stored per session.
        // This allows for Key renewing without needing to force the entire Session from being destroyed.
        // This Also helps prevent impersonation attempts.
        .with_security_mode(axum_session::SecurityMode::PerSession);

    let session_store = axum_session::SessionStore::<axum_session::SessionPgPool>::new(None, session_config);
    session_store.initiate().await.unwrap();
    axum_session::SessionLayer::new(session_store)
}


pub async fn init_app(configuration: &WebConfig) -> axum::Router
{
    if let Some(value) = &configuration.locale {
        rust_i18n::set_locale(value);
    }

    if let Some(value) = &configuration.product {
        let db_url = deal_db_url(&value.db);
        crate::db::init_rbatis(&db_url);
    } else { panic!("database connect error") }

    let session_layer = init_session_layer().await;
    axum::Router::new()
        .nest("/user", user::config_router())
        .nest("/article", article::config_router())
        .layer(session_layer)
}


pub async fn init_app_dev(configuration: &WebConfig) -> axum::Router
{
    if let Some(value) = &configuration.locale {
        rust_i18n::set_locale(value);
    }

    if let Some(value) = &configuration.dev {
        let db_url = deal_db_url(&value.db);
        crate::db::init_rbatis(&db_url);
    } else { panic!("database connect error") }

    let session_layer = init_session_layer().await;
    axum::Router::new()
        .nest("/user", user::config_router())
        .nest("/article", article::config_router())
        .layer(session_layer)
}



pub async fn start_server(configuration: WebConfig, router: axum::Router)
{
    let server = configuration.product.unwrap().server;
    let addr = std::net::SocketAddr::from((server.address, server.port));
    axum::Server::bind(&addr)
        .serve(router.into_make_service())
        .await
        .unwrap();
}

pub async fn start_server_dev(configuration: WebConfig, router: axum::Router)
{
    let server = configuration.dev.unwrap().server;
    let addr = std::net::SocketAddr::from((server.address, server.port));
    axum::Server::bind(&addr)
        .serve(router.into_make_service())
        .await
        .unwrap();
}





#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_toml_null_value()
    {
        let configuration = read_web_configuration("./web.toml");
        let value = if let Some(value) = configuration.locale {
            value
        } // else { "null".to_string() }
        else { rust_i18n::locale() };
        println!("locale = '{}'", value);
    }

}


